home *** CD-ROM | disk | FTP | other *** search
- /*
- ** $RCSfile: FFSsupport.c,v $
- ** $Filename: FFSsupport.c $
- ** $Revision: 0.0 $
- ** $Date: $
- **
- ** Support functions for low-level access to FFS on-disk data structures
- ** (version 0.1)
- **
- ** (C) Copyright 2002 by Etienne Vogt
- */
-
- #define __USE_SYSBASE
- #include <exec/io.h>
- #include <dos/dos.h>
- #include <devices/newstyle.h>
- #include <devices/trackdisk.h>
- #include <proto/exec.h>
- #include <proto/dos.h>
- #include <clib/alib_protos.h>
- #include <string.h>
- #include "ffs.h"
-
- #define io_HighOffset io_Actual
-
- /* external global variables */
-
- extern struct ExecBase *SysBase;
- extern struct DosLibrary *DOSBase;
- extern struct IOStdReq *DeviceIO;
- extern struct DiskDevice mydiskdev;
-
- /* internal function prototypes */
-
- static int readbitmaptable(ULONG **bitmap, ULONG *bmtable, int off_start, int off_end, ULONG size, ULONG *sizeleft);
- static BOOL check64bit(struct IOStdReq *ioreq);
- static UBYTE __inline capitalch(UBYTE ch);
- static STRPTR __inline hstr2c(UBYTE *hstr);
-
- /* initFFS: initialise some fields in DiskDevice structure and check for need and **
- ** availability of 64bit commands. **
- ** This function must be called before using any other FFSsupport function. **
- ** The exec device must have been properly opened using the DeviceIO IoStdReq **
- ** before calling this function. */
-
- int initFFS(void)
- { ULONG cylsize512;
- int error=0;
-
- mydiskdev.dd_SectorsPerCyl = mydiskdev.dd_SectorsPerTrack * mydiskdev.dd_Surfaces;
- mydiskdev.dd_HighKey = ((mydiskdev.dd_HighCyl - mydiskdev.dd_LowCyl + 1) * mydiskdev.dd_SectorsPerCyl) /
- mydiskdev.dd_SectorsPerBlock - 1;
- mydiskdev.dd_BlockSizeL = mydiskdev.dd_SectorSize * mydiskdev.dd_SectorsPerBlock / 4;
- mydiskdev.dd_RootKey = (mydiskdev.dd_HighKey + mydiskdev.dd_Reserved) / 2;
-
- cylsize512 = mydiskdev.dd_SectorsPerCyl * mydiskdev.dd_SectorSize / 512;
-
- if (mydiskdev.dd_HighCyl * cylsize512 >= 8*1024*1024)
- { if (mydiskdev.dd_Flags & DDFF_VERBOSE) PutStr("initFFS: Partition extends beyond the first 4 Gb of the drive\n");
- if (check64bit(DeviceIO))
- { if (mydiskdev.dd_Flags & DDFF_VERBOSE) PutStr("initFFS: Using NSD 64bit addressing\n\n");
- mydiskdev.dd_Flags |= DDFF_DO64BIT;
- }
- else
- { PutStr("initFFS: 64bit addressing is required but not available\n");
- error = ERROR_OBJECT_TOO_LARGE;
- }
- }
-
- return error;
- }
-
- static BOOL check64bit(struct IOStdReq *ioreq)
- { struct NSDeviceQueryResult *NSDQuery;
- BOOL is64bit=FALSE;
- UWORD *cmdcheck;
-
- if (NSDQuery = (struct NSDeviceQueryResult *)AllocVec(sizeof(struct NSDeviceQueryResult), MEMF_PUBLIC|MEMF_CLEAR))
- { ioreq->io_Command = NSCMD_DEVICEQUERY;
- ioreq->io_Data = NSDQuery;
- ioreq->io_Length = sizeof(struct NSDeviceQueryResult);
- if (!DoIO((struct IORequest *)ioreq) && ioreq->io_Actual >= 16 && ioreq->io_Actual <= sizeof(struct NSDeviceQueryResult)
- && NSDQuery->SizeAvailable == ioreq->io_Actual)
- { if (NSDQuery->DeviceType == NSDEVTYPE_TRACKDISK)
- { for (cmdcheck = NSDQuery->SupportedCommands ; *cmdcheck ; cmdcheck++)
- { if (*cmdcheck == NSCMD_TD_READ64) is64bit = TRUE;
- }
- }
- else Printf("Not a trackdisk-like device (type %ld)\n", NSDQuery->DeviceType);
- }
- else PutStr("NSCMD_DEVICEQUERY failed !\n");
- FreeVec(NSDQuery);
- }
- else PutStr("No memory for NSDeviceQueryResult structure");
- return is64bit;
- }
-
- /* readblock: read a block from an FFS partition given by its KeyID number. **
- ** The block is returned as an ULONG * and must be properly FreeVec()ed by the **
- ** caller when no longer needed. **
- ** NULL is returned in case of an error, with the error code in DeviceIO->io_Error */
-
- ULONG *readblock(ULONG KeyID)
- { ULONG blocksize = mydiskdev.dd_BlockSizeL * 4;
- ULONG highsects = ((1L << 23) / mydiskdev.dd_SectorSize) * 512;
- ULONG *buffer;
- int error;
-
- if (buffer = AllocVec(blocksize, mydiskdev.dd_BufMemType))
- { DeviceIO->io_Data = buffer;
- DeviceIO->io_Length = blocksize;
- DeviceIO->io_Offset = mydiskdev.dd_LowCyl * mydiskdev.dd_SectorsPerCyl * mydiskdev.dd_SectorSize +
- KeyID * blocksize; /* Compute lower 32bits offset */
- if (mydiskdev.dd_Flags & DDFF_DO64BIT)
- { DeviceIO->io_Command = NSCMD_TD_READ64;
- DeviceIO->io_HighOffset = (mydiskdev.dd_LowCyl * mydiskdev.dd_SectorsPerCyl + KeyID * mydiskdev.dd_SectorsPerBlock) /
- highsects;
- }
- else DeviceIO->io_Command = CMD_READ;
- if (error = DoIO((struct IORequest *)DeviceIO))
- { FreeVec(buffer);
- buffer = NULL;
- }
- }
- else DeviceIO->io_Error = TDERR_NoMem;
- return buffer;
- }
-
- /* writeblock: write a block back to an FFS partition given by its KeyID number. **
- ** The checksum must have been computed previously with sumblock() if required. **
- ** If the write fails, the error code is returned. */
-
- int writeblock(ULONG *buffer, ULONG KeyID)
- { ULONG blocksize = mydiskdev.dd_BlockSizeL * 4;
- ULONG highsects = ((1L << 23) / mydiskdev.dd_SectorSize) * 512;
-
- DeviceIO->io_Data = buffer;
- DeviceIO->io_Length = blocksize;
- DeviceIO->io_Offset = mydiskdev.dd_LowCyl * mydiskdev.dd_SectorsPerCyl * mydiskdev.dd_SectorSize +
- KeyID * blocksize; /* Compute lower 32bits offset */
- if (mydiskdev.dd_Flags & DDFF_DO64BIT)
- { DeviceIO->io_Command = NSCMD_TD_WRITE64;
- DeviceIO->io_HighOffset = (mydiskdev.dd_LowCyl * mydiskdev.dd_SectorsPerCyl + KeyID * mydiskdev.dd_SectorsPerBlock) /
- highsects;
- }
- else DeviceIO->io_Command = CMD_WRITE;
-
- return DoIO((struct IORequest *)DeviceIO);
- }
-
- /* checkblock: perform basic consistency checks on a filesystem block according to the **
- ** expected types and subtypes. If type and/or subtype are set to 0, the **
- ** corresponding checks are skipped. If type is set to 0, the checksum test is **
- ** also skipped. A non-zero return code is returned in case of failure. */
-
- int checkblock(ULONG *buffer, ULONG KeyID, int type, int sectype)
- { ULONG chksum = 0;
- int i;
-
- if (type)
- if (buffer[FILE_TYPE] != type) return ERR_BADTYPE;
- if (type == T_SHORT && sectype)
- if (buffer[mydiskdev.dd_BlockSizeL+FILE_SECTYPE] != sectype) return ERR_BADSECTYPE;
- if ((type == T_SHORT && sectype != ST_ROOT) || type == T_LIST)
- if (buffer[FILE_OWNKEY] != KeyID) return ERR_BLKCORRUPT;
- else if (type == T_SHORT && sectype == ST_ROOT)
- if (buffer[FILE_OWNKEY]) return ERR_BLKCORRUPT;
- if (type)
- { for (i = 0 ; i < mydiskdev.dd_BlockSizeL ; i++) chksum += buffer[i];
- if (chksum) return ERR_BADCHKSUM;
- }
-
- return 0;
- }
-
- /* checkbmblock: verify the checksum of a bitmap block. ERR_BADCHKSUM is returned in **
- ** case of failure. */
-
- int checkbmblock(ULONG *buffer)
- { ULONG chksum = 0;
- int i;
-
- for (i = 0 ; i < mydiskdev.dd_BlockSizeL ; i++) chksum += buffer[i];
- if (chksum) return ERR_BADCHKSUM;
- else return 0;
- }
-
- /* readbitmap: read the volume bitmap into memory from the pointers in the volume **
- ** rootblock. The bitmap is returned as an array of ULONG (bit 0 of first ULONG **
- ** is for the first non-reserved block). The array must be FreeVec()ed when no **
- ** longer needed. NULL is returned in case of error. */
-
- ULONG *readbitmap(ULONG *rootblock)
- { ULONG bitmapsize = ((mydiskdev.dd_HighKey - mydiskdev.dd_Reserved + 32) / 32) * 4;
- ULONG sizeleft = bitmapsize;
- ULONG bmextkey;
- ULONG *bitmap,*pbitmap,*bitmapext;
- int error;
-
- if (bitmap = AllocVec(bitmapsize, MEMF_ANY))
- { pbitmap = bitmap;
-
- if (error = readbitmaptable(&pbitmap, rootblock, mydiskdev.dd_BlockSizeL+ROOT_BITMAPBLOCKS, mydiskdev.dd_BlockSizeL+ROOT_BITMAPEXT, bitmapsize, &sizeleft))
- { Printf("readbitmap: error %ld\n", error);
- FreeVec(bitmap);
- return NULL;
- }
-
- bmextkey = rootblock[mydiskdev.dd_BlockSizeL+ROOT_BITMAPEXT];
- while (sizeleft)
- { if (bmextkey > mydiskdev.dd_HighKey || bmextkey < mydiskdev.dd_Reserved)
- { Printf("readbitmap: bitmap extension block %lu is out of range\n", bmextkey);
- FreeVec(bitmap);
- return NULL;
- }
- if (bitmapext = readblock(bmextkey))
- { if (error = readbitmaptable(&pbitmap, bitmapext, BEXT_POINTERS, mydiskdev.dd_BlockSizeL+BEXT_EXTENSION, bitmapsize, &sizeleft))
- { Printf("readbitmap: error %ld\n", error);
- FreeVec(bitmapext);
- FreeVec(bitmap);
- return NULL;
- }
- bmextkey = bitmapext[mydiskdev.dd_BlockSizeL+BEXT_EXTENSION];
- FreeVec(bitmapext);
- }
- else
- { Printf("readbitmap: couldn't read bitmap extension block %lu (error %ld)\n", bmextkey, DeviceIO->io_Error);
- FreeVec(bitmap);
- return NULL;
- }
- }
- }
- else Printf("readbitmap: couldn't allocate %lu bytes for bitmap\n", bitmapsize);
-
- return bitmap;
- }
-
- static int readbitmaptable(ULONG **bitmap, ULONG *bmtable, int off_start, int off_end, ULONG size, ULONG *sizeleft)
- { UWORD bmblocksize;
- ULONG *bmblock;
- ULONG bmkey;
- int i, error;
-
- for (i = off_start ; i < off_end ; i++)
- { bmkey = bmtable[i];
- if (bmkey > mydiskdev.dd_HighKey || bmkey < mydiskdev.dd_Reserved)
- { Printf("readbitmap: bitmap block %lu is out of range\n", bmkey);
- return ERR_INVBLKNUM;
- }
- if (bmblock = readblock(bmkey))
- { if (error = checkbmblock(bmblock))
- { Printf("readbitmap: Invalid bitmap block %lu\n", bmkey);
- FreeVec(bmblock);
- return error;
- }
- bmblocksize = 4 * (mydiskdev.dd_BlockSizeL - 1);
- if (bmblocksize > *sizeleft) bmblocksize = *sizeleft;
- CopyMemQuick(&bmblock[BITMAP_BITMAPDATA], *bitmap, bmblocksize);
- *bitmap += bmblocksize / 4;
- *sizeleft -= bmblocksize;
- FreeVec(bmblock);
- if (*sizeleft == 0) break;
- }
- else
- { Printf("readbitmap: Couldn't read bitmap block %lu\n", bmkey);
- return DeviceIO->io_Error;
- }
- }
- return 0;
- }
-
- /* findfreeblock: Return the KeyID of the first free block found in the in-memory copy **
- ** of the bitmap, starting at StartKey. Zero is returned if all blocks are in use. */
-
- ULONG findfreeblock(ULONG *bitmap, ULONG StartKey)
- { ULONG bitmapsizel = (mydiskdev.dd_HighKey - mydiskdev.dd_Reserved + 32) / 32;
- ULONG key=0;
- int startlong, startbit, i, j;
-
- if (StartKey == 0)
- { startlong = 0;
- startbit = 0;
- }
- else
- { startlong = (StartKey - mydiskdev.dd_Reserved) / 32;
- startbit = (StartKey - mydiskdev.dd_Reserved) % 32;
- }
-
- for (i = startlong ; i < bitmapsizel ; i++)
- { if (bitmap[i] == 0) continue;
- else
- { for (j = startbit ; j < 32 ; j++)
- { startbit = 0;
- if ((bitmap[i] & (1L << j)) == 0) continue;
- else
- { key = mydiskdev.dd_Reserved + 32*i + j;
- break;
- }
- }
- }
- if (key) break;
- }
-
- if (key > mydiskdev.dd_HighKey) key = 0;
- return key;
- }
-
- /* findcontigfreeblocks: Search for a range of n contiguous free blocks in the **
- ** in-memory copy of the bitmap and return the starting KeyID or zero if there are **
- ** not enough contiguous free blocks available. */
-
- ULONG findcontigfreeblocks(ULONG *bitmap, int numblocks)
- { ULONG bitmapsizel = (mydiskdev.dd_HighKey - mydiskdev.dd_Reserved + 32) / 32;
- ULONG StartKey, key=0;
- int startlong, startbit, remain, i, j;
- BOOL skip;
-
- while (StartKey = findfreeblock(bitmap, key))
- { startlong = (StartKey + 1 - mydiskdev.dd_Reserved) / 32;
- startbit = (StartKey + 1 - mydiskdev.dd_Reserved) % 32;
- remain = numblocks - 1;
- skip = FALSE;
-
- for (i = startlong ; i < bitmapsizel ; i++)
- { for (j = startbit ; j < 32 ; j++)
- { startbit = 0;
- if (bitmap[i] & (1L << j)) remain--;
- else
- { skip = TRUE;
- break;
- }
- if (!remain) break;
- }
- if (skip || !remain) break;
- }
-
- if ((key = mydiskdev.dd_Reserved + 32*i + j) > mydiskdev.dd_HighKey)
- { StartKey = 0;
- break;
- }
- if (!remain) break;
- }
-
- return StartKey;
- }
-
- /* allocblock: Mark a block as allocated in the on-memory bitmap and return its **
- ** previous state (TRUE = free). Updating the on-disk bitmap is best left to the **
- ** disk validator once all low-level changes are done. */
-
- BOOL allocblock(ULONG *bitmap, ULONG KeyID)
- { int bmlong = (KeyID - mydiskdev.dd_Reserved) / 32;
- int bmbit = (KeyID - mydiskdev.dd_Reserved) % 32;
- BOOL wasfree;
-
- if (bitmap[bmlong] & (1L << bmbit)) wasfree = TRUE;
- else wasfree = FALSE;
-
- bitmap[bmlong] &= ~(1L << bmbit);
-
- return wasfree;
- }
-
- /* freeblock: Mark a block as free in the on-memory bitmap and return its previous **
- ** state (TRUE = free). Updating the on-disk bitmap is best left to the disk **
- ** validator once all low-level changes are done. */
-
- BOOL freeblock(ULONG *bitmap, ULONG KeyID)
- { int bmlong = (KeyID - mydiskdev.dd_Reserved) / 32;
- int bmbit = (KeyID - mydiskdev.dd_Reserved) % 32;
- BOOL wasfree;
-
- if (bitmap[bmlong] & (1L << bmbit)) wasfree = TRUE;
- else wasfree = FALSE;
-
- bitmap[bmlong] |= (1L << bmbit);
-
- return wasfree;
- }
-
- /* isfreeblock: Check if a given block is marked as free in the in-memory bitmap. */
-
- BOOL isfreeblock(ULONG *bitmap, ULONG KeyID)
- { int bmlong = (KeyID - mydiskdev.dd_Reserved) / 32;
- int bmbit = (KeyID - mydiskdev.dd_Reserved) % 32;
-
- if (bitmap[bmlong] & (1L << bmbit)) return TRUE;
- else return FALSE;
- }
-
- /* initheader: Initializes a file header in memory with given KeyID, ParentKey, type, **
- ** subtype and name (all other fields will be zero). Returns a pointer to the **
- ** initialized block or NULL for failure. **
- ** The block MUST be checksummed with sumblock() before being written back to disk */
-
- ULONG *initheader(ULONG KeyId, ULONG ParentKey, int type, int sectype, STRPTR name)
- { ULONG blocksize = mydiskdev.dd_BlockSizeL * 4;
- ULONG *header;
- BYTE namelen, *nameptr;
-
- if (header = AllocVec(blocksize, mydiskdev.dd_BufMemType|MEMF_CLEAR))
- { header[FILE_TYPE] = type;
- header[mydiskdev.dd_BlockSizeL+FILE_SECTYPE] = sectype;
- if ((type == T_SHORT && sectype != ST_ROOT) || type == T_LIST)
- { header[FILE_OWNKEY] = KeyId;
- header[mydiskdev.dd_BlockSizeL+FILE_PARENT] = ParentKey;
- }
- if (type == T_SHORT && name)
- { namelen = strlen(name);
- if (namelen > 30) namelen = 30;
- nameptr = (UBYTE *)&header[mydiskdev.dd_BlockSizeL+FILE_NAME];
- nameptr[0] = namelen;
- strncpy(&nameptr[1], name, 30);
- }
- }
-
- return header;
- }
-
- /* clearblockptrs: clear the block pointers in a file header or file extension block */
-
- void clearblockptrs(ULONG *header)
- { int i;
-
- header[FILE_BLOCKCOUNT] = 0;
- header[FILE_FIRSTBLOCK] = 0;
- for (i = mydiskdev.dd_BlockSizeL+FILE_DATABLOCK1 ; i >= FILE_DATABLOCKS ; i--) header[i] = 0;
- }
-
- /* sumblock: compute an FFS metadata block checksum so the block can be written back to **
- ** disk. */
-
- void sumblock(ULONG *buffer)
- { ULONG chksum=0;
- int i;
-
- buffer[FILE_CHECKSUM] = 0;
- for (i = 0 ; i < mydiskdev.dd_BlockSizeL ; i++) chksum += buffer[i];
- buffer[FILE_CHECKSUM] = -chksum;
- }
-
- /* hashname: compute the hash index relative to the start of the directory block for **
- ** the given file name */
-
- ULONG hashname(STRPTR name)
- { ULONG hash = strlen(name);
-
- while (*name)
- hash = (hash * 13 + capitalch(*name++)) & 0x7ff;
-
- return hash % (mydiskdev.dd_BlockSizeL - 56) + UDIR_HASHTABLE;
- }
-
- static UBYTE __inline capitalch(UBYTE ch)
- { if ((mydiskdev.dd_DOSType & 0xff) < 2)
- return (UBYTE)(ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch);
- else
- return (UBYTE)(ch >= '\340' && ch <= '\376' && ch != '\367' ||
- ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch);
- }
-
- /* gethashchain: return the hash chain pointer needed for inserting a given header into **
- ** a given directory block. Since 0 is a valid result, (ULONG)(-1) is returned to **
- ** indicate an error. Check explicitely for this value for an error ! **
- ** Assumes the hash chain is correctly sorted by ascending key ID as must be the **
- ** case with FFS (for OFS it doesn't really matter so we do as if it was FFS). */
-
- ULONG gethashchain(ULONG *dirblock, ULONG *headerblock)
- { ULONG hash = hashname(hstr2c((UBYTE *)&headerblock[mydiskdev.dd_BlockSizeL+FILE_NAME]));
- ULONG key;
- ULONG *block;
- int error;
-
- if ((key = dirblock[hash]) == 0) return 0;
- else
- { while (headerblock[FILE_OWNKEY] > key)
- { if (block = readblock(key))
- { if (error = checkblock(block, key, T_SHORT, 0))
- { Printf("gethashchain: Invalid header block %lu (error %ld)\n", key, error);
- FreeVec(block);
- return (ULONG)(-1L);
- }
- key = block[mydiskdev.dd_BlockSizeL+FILE_HASHCHAIN];
- FreeVec(block);
- }
- else
- { Printf("gethashchain: Couldn't read header block %lu (error %ld)\n", key, DeviceIO->io_Error);
- return (ULONG)(-1L);
- }
- }
- }
-
- return key;
- }
-
- /* hashlink: link a given header into a directory hash chain. The header should be **
- ** valid, in particular, its hashchain pointer should be set via a previous call **
- ** to gethashchain() and it should have been written back to disk via writeblock() **
- ** so that the risk of disk structure corruption is minimized. */
-
- int hashlink(ULONG *dirblock, ULONG *headerblock)
- { ULONG hash = hashname(hstr2c((UBYTE *)&headerblock[mydiskdev.dd_BlockSizeL+FILE_NAME]));
- ULONG key, headerkey = headerblock[FILE_OWNKEY];
- ULONG *block;
- int error;
-
- if ((key = dirblock[hash]) == 0 || key > headerkey) dirblock[hash] = headerkey;
- else
- { while (key)
- { if (block = readblock(key))
- { if (error = checkblock(block, key, T_SHORT, 0))
- { Printf("hashlink: Invalid header block %lu (error %ld)\n", key, error);
- FreeVec(block);
- return error;
- }
- if ((key = block[mydiskdev.dd_BlockSizeL+FILE_HASHCHAIN]) > headerkey) break;
- }
- else
- { Printf("hashlink: Couldn't read header block %lu (error %ld)\n", key, DeviceIO->io_Error);
- return DeviceIO->io_Error;
- }
- }
- block[mydiskdev.dd_BlockSizeL+FILE_HASHCHAIN] = headerkey;
- sumblock(block);
- if (error = writeblock(block, block[FILE_OWNKEY]))
- { Printf("hashlink: Couldn't write header block %lu (error %ld)\n", block[FILE_OWNKEY], error);
- FreeVec(block);
- return error;
- }
- FreeVec(block);
- }
-
- DateStamp((struct DateStamp *)&dirblock[mydiskdev.dd_BlockSizeL+UDIR_DAYS]);
- sumblock(dirblock);
-
- if ((key = dirblock[UDIR_OWNKEY]) == 0) key = mydiskdev.dd_RootKey;
-
- if (error = writeblock(dirblock, key))
- { Printf("hashlink: Couldn't write directory block %lu (error %ld)\n", key, error);
- return error;
- }
-
- return 0;
- }
-
- static STRPTR __inline hstr2c(UBYTE *hstr)
- { static char buffer[256];
- int n;
-
- if ((n = *hstr++) == 0) return NULL;
- strncpy(buffer, hstr, n);
- buffer[n] = 0;
- return buffer;
- }
-